package com.chenyi.pay.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.chenyi.common.utils.ServletUtils;
import com.chenyi.common.utils.StringUtils;
import com.chenyi.common.utils.XmlUtils;
import com.chenyi.common.utils.http.HttpUtils;
import com.chenyi.common.utils.ip.IpUtils;
import com.chenyi.pay.bean.AlipayBean;
import com.chenyi.pay.bean.WxpayBean;
import com.chenyi.pay.config.AliPayConfig;
import com.chenyi.pay.config.WxPayConfig;
import com.chenyi.pay.exception.WxPayApiException;
import com.chenyi.pay.service.PayService;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConfig;
import com.github.wxpay.sdk.WXPayUtil;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;

/**
 * 支付服务 实现
 * @author wangkh
 * @date 2020/10/27 0027  16:35
 */
@Slf4j
@Service
public class PayServiceImpl implements PayService {

    @Override
    public String aliPayAsWeb(AlipayBean alipayBean) throws AlipayApiException {
        //1、获得初始化的AlipayClient
        AlipayClient alipayClient = getDefaultAlipayClient();
        //2、设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        //页面跳转同步通知页面路径
        alipayRequest.setReturnUrl(AliPayConfig.getReturnUrl());
        // 服务器异步通知页面路径
        alipayRequest.setNotifyUrl(AliPayConfig.getNotifyUrl());
        //封装参数
        alipayRequest.setBizContent(JSON.toJSONString(alipayBean));
        //3、请求支付宝进行付款，并获取支付结果
        String result = alipayClient.pageExecute(alipayRequest).getBody();
        log.debug("aliPayAsWeb:"+result);
        //返回付款信息
        return  result;
    }

    @Override
    public String aliPayAsApp(AlipayBean alipayBean) throws AlipayApiException {
        //1、获得初始化的AlipayClient
        AlipayClient alipayClient = getDefaultAlipayClient();
        //2、设置请求参数
        AlipayTradeAppPayRequest alipayRequest = new AlipayTradeAppPayRequest();
        //页面跳转同步通知页面路径
        alipayRequest.setReturnUrl(AliPayConfig.getReturnUrl());
        // 服务器异步通知页面路径
        alipayRequest.setNotifyUrl(AliPayConfig.getNotifyUrl());
        //封装参数
        alipayRequest.setBizContent(JSON.toJSONString(alipayBean));
        //3、请求支付宝进行付款，并获取支付结果
        String result = alipayClient.sdkExecute(alipayRequest).getBody();
        log.debug("aliPayAsApp:"+result);
        //返回付款信息
        return  result;
    }

    @Override
    public String aliPayAsScan(AlipayBean alipayBean) throws AlipayApiException {
        //1、获得初始化的AlipayClient
        AlipayClient alipayClient = getDefaultAlipayClient();
        //2、设置请求参数
        AlipayTradePrecreateRequest alipayRequest = new AlipayTradePrecreateRequest();
        //页面跳转同步通知页面路径
        alipayRequest.setReturnUrl(AliPayConfig.getReturnUrl());
        // 服务器异步通知页面路径
        alipayRequest.setNotifyUrl(AliPayConfig.getNotifyUrl());
        //封装参数
        alipayRequest.setBizContent(JSON.toJSONString(alipayBean));

        //3、请求支付宝进行付款，并获取支付结果
        String result = alipayClient.execute(alipayRequest, "GET").getQrCode();
        log.debug("aliPayAsScan:"+result);
        //返回付款信息
        return  result;
    }


    private AlipayClient getDefaultAlipayClient(){
        return new DefaultAlipayClient(
                AliPayConfig.getGatewayUrl(),//支付宝网关
                AliPayConfig.getAppId(),//appid
                AliPayConfig.getMerchantPrivateKey(),//商户私钥
                "json",
                AliPayConfig.getCharset(),//字符编码格式
                AliPayConfig.getAlipayPublicKey(),//支付宝公钥
                AliPayConfig.getSignType()//签名方式
        );
    }

    @Override
    public String wxPayApp(WxpayBean wxpayBean) throws WxPayApiException {
        //交易类型 设置
        wxpayBean.setTrade_type("APP");
        Map<String, String> xmlResult = wxPayUnifiedOrder(wxpayBean,WxPayConfig.getAppId());
        Map<String,String> payRequest = Maps.newHashMap();
        payRequest.put("appid",WxPayConfig.getAppId());
        payRequest.put("partnerid",WxPayConfig.getMchId());
        payRequest.put("prepayid",xmlResult.get("prepay_id"));
        payRequest.put("package","Sign=WXPay");
        payRequest.put("noncestr", WXPayUtil.generateNonceStr());
        payRequest.put("timestamp",new Date().getTime() / 1000 + "");
        try {
            payRequest.put("sign",WXPayUtil.generateSignature(new TreeMap<>(payRequest),WxPayConfig.getKey()));
        }catch (Exception e){
            throw new WxPayApiException("微信支付签名异常："+e.getMessage());
        }
        return JSON.toJSONString(payRequest);
    }

    @Override
    public String wxPayAsMiniapp(WxpayBean wxpayBean) throws WxPayApiException {
        //交易类型 设置
        wxpayBean.setTrade_type("JSAPI");
        Map<String, String> xmlResult = wxPayUnifiedOrder(wxpayBean,WxPayConfig.getMiniAppId());
        Map<String,String> payRequest = Maps.newHashMap();
        payRequest.put("appId",WxPayConfig.getMiniAppId());
        payRequest.put("timeStamp",new Date().getTime() / 1000 + "");
        payRequest.put("nonceStr", WXPayUtil.generateNonceStr());
        payRequest.put("package","prepay_id=" +xmlResult.get("prepay_id"));
        payRequest.put("signType","MD5");
        try {
            payRequest.put("paySign",WXPayUtil.generateSignature(new TreeMap<>(payRequest),WxPayConfig.getKey()));
        }catch (Exception e){
            throw new WxPayApiException("微信支付签名异常："+e.getMessage());
        }
        return JSON.toJSONString(payRequest);
    }

    @Override
    public String wxPayAsScan(WxpayBean wxpayBean) throws WxPayApiException {
        //交易类型 设置
        wxpayBean.setTrade_type("NATIVE");
        Map<String, String> xmlResult = wxPayUnifiedOrder(wxpayBean,WxPayConfig.getWebAppId());
        return xmlResult.get("code_url");
    }


    private Map<String, String> wxPayUnifiedOrder(WxpayBean wxpayBean,String appId) throws WxPayApiException{
        WXPayConfig wxPayConfig = null;
        // 拼接统一下单地址参数
        try {
            wxPayConfig = getWXPayConfig(appId);
        }catch (Exception e){
            throw new WxPayApiException("(微信配置异常)"+e.getMessage());
        }
        try {
            WXPay wxPay = new WXPay(wxPayConfig);
            Map<String, String> paraMap = JSON.parseObject(JSON.toJSONString(wxpayBean),new TypeReference<HashMap<String,String>>() {});
            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
            paraMap.put("fee_type","CNY");
            paraMap.put("spbill_create_ip",ip);
            paraMap.put("notify_url",WxPayConfig.getNotifyUrl());
            paraMap.put("trade_type",wxpayBean.getTrade_type());
            Map<String, String> xmlResult = wxPay.unifiedOrder(paraMap);
            if (StringUtils.isNotEmpty(xmlResult.get("return_code")) && !xmlResult.get("return_code").equals("SUCCESS")){
                throw new WxPayApiException("(微信异常信息)"+xmlResult.get("return_msg"));
            }
            return xmlResult;
        }catch (Exception e){
            throw new WxPayApiException(e.getMessage());
        }
    }


    /* 功能描述: <br>
     * 〈获取微信支付配置〉
     *
     * @param appId
     * @return: com.github.wxpay.sdk.WXPayConfig
     * @since: 1.0.0
     * @Author: wangkh
     * @Date: 2020/10/27 0027 18:57
     */
    private WXPayConfig getWXPayConfig(String appId) throws Exception{
        InputStream certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("cert/wxpay/apiclient_cert.p12");
        byte[] certData = IOUtils.toByteArray(certStream);
        certStream.close();
        return new WXPayConfig() {


            @Override
            public String getAppID() {
                return appId;
            }

            @Override
            public String getMchID() {
                return WxPayConfig.getMchId();
            }

            @Override
            public String getKey() {
                return WxPayConfig.getKey();
            }

            @Override
            public InputStream getCertStream() {
                return new ByteArrayInputStream(certData);
            }

            @Override
            public int getHttpConnectTimeoutMs() {
                return 0;
            }

            @Override
            public int getHttpReadTimeoutMs() {
                return 0;
            }
        };
    }
}
